#include "Text.h"
#include "Font.h"
#include "FontShader.h"
#include "NeroCore\NMemoryManager.h"
#include "NeroGame\NDirector.h"

using namespace Graphics;

#define Y_OFFSET 25

Font* Text::mFont = NULL;
FontShader* Text::mFontShader = NULL;

Text::Text(void)
	: mMaxLength(32)
{
	mTextObj.Sentence = NULL;
	mTextObj.XPosition = 0;
	mTextObj.YPosition = 0;
}

Text::~Text(void)
{
	Deinitialize();
}

bool Text::Initialize(HWND hwnd, int screenWidth, int screenHeight, D3DXMATRIX baseViewMatrix)
{
	bool result;
	// Store the screen width and height.
	mScreenWidth = screenWidth;
	mScreenHeight = screenHeight;

	// Store the base view matrix.
	mBaseViewMatrix = baseViewMatrix;

	// Create the font object.
	if(!mFont)
	{
		mFont = MY_NEW(Memory::HID_Rendering,"Font") Font();

		// Initialize the font object.
		result = mFont->Initialize("..\\..\\assets\\ini\\fontdata.txt", "..\\..\\assets\\textures\\font.dds");
		if(!result)
		{
			MessageBox(hwnd, "Could not initialize the font object.", "Error", MB_OK);
			return false;
		}
	}

	// Create the font shader object.
	if(!mFontShader)
	{
		mFontShader = MY_NEW(Memory::HID_Rendering,"Font Shader") FontShader();
		// Initialize the font shader object.
		result = mFontShader->Initialize();
		if(!result)
		{
			MessageBox(hwnd, "Could not initialize the font shader object.", "Error", MB_OK);
			return false;
		}
	}


	if(!InitializeSentenceType(32))
	{
		MessageBox(hwnd, "Could not initialize SentenceType data", "Error", MB_OK);
	}


	return true;
}

void Text::Deinitialize()
{
	// Release the font shader object.
	if(mFontShader)
	{
		mFontShader->Deinitialize();
		MY_DELETE(mFontShader);
		mFontShader = NULL;
	}

	// Release the font object.
	if(mFont)
	{
		mFont->Deinitialize();
		MY_DELETE(mFont);
		mFont = NULL;
	}

	if(mTextObj.Sentence)
	{
		mTextObj.Sentence->vertexBuffer->Release();
		mTextObj.Sentence->vertexBuffer = NULL;

		mTextObj.Sentence->indexBuffer->Release();
		mTextObj.Sentence->indexBuffer = NULL;

		MY_DELETE(mTextObj.Sentence);
		mTextObj.Sentence = NULL;
	}

	return;
}

void Text::Update(float gameTime)
{
}

bool Text::Render()
{
	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	D3DXMATRIX worldMatrix = device->GetWorldMatrix();
	D3DXMATRIX orthoMatrix = device->GetOrthoMatrix();

	if(!UpdateSentenceType(mTextObj.Text.c_str(), mTextObj.XPosition, mTextObj.YPosition, mTextObj.TextColor))
	{
		MessageBox(device->GetWindowsSettings().WindowHandle, "Could not UpdateSentenceType SentenceType data", "Error", MB_OK);
	}
	
	// Set vertex buffer stride and offset.
	unsigned int stride = sizeof(FontVertexType); 
	unsigned int offset = 0;

	// Set the vertex buffer to active in the input assembler so it can be rendered.
	device->GetDeviceContext()->IASetVertexBuffers(0, 1, &mTextObj.Sentence->vertexBuffer, &stride, &offset);

	// Set the index buffer to active in the input assembler so it can be rendered.
	device->GetDeviceContext()->IASetIndexBuffer(mTextObj.Sentence->indexBuffer, DXGI_FORMAT_R32_UINT, 0);

	// Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
	device->GetDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	// Render the text using the font shader.
	mFontShader->Render(mTextObj.Sentence->indexCount, worldMatrix, mBaseViewMatrix, orthoMatrix, mFont->GetTexture(), mTextObj.TextColor);

	return true;
}

bool Text::InitializeSentenceType(int maxLength)
{
	FontVertexType* vertices;
	unsigned long* indices;
	D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
	D3D11_SUBRESOURCE_DATA vertexData, indexData;
	HRESULT result;

	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();

	mTextObj.Sentence = MY_NEW(Memory::HID_Rendering,"Text Sentence") SentenceType();

	// Initialize the sentence buffers to null.
	mTextObj.Sentence->vertexBuffer = NULL;
	mTextObj.Sentence->indexBuffer = NULL;

	// Set the maximum length of the sentence.
	mTextObj.Sentence->maxLength = maxLength;

	// Set the number of vertices in the vertex array.
	mTextObj.Sentence->vertexCount = 6 * maxLength;

	// Set the number of indexes in the index array.
	mTextObj.Sentence->indexCount = mTextObj.Sentence->vertexCount;

	// Create the vertex array.
	vertices = new FontVertexType[mTextObj.Sentence->vertexCount];
	if(!vertices)
	{
		return false;
	}

	// Create the index array.
	indices = new unsigned long[mTextObj.Sentence->indexCount];
	if(!indices)
	{
		return false;
	}

	// Initialize vertex array to zeros at first.
	memset(vertices, 0, (sizeof(FontVertexType) * mTextObj.Sentence->vertexCount));

	// Initialize the index array.
	for(int i = 0; i < mTextObj.Sentence->indexCount; i++)
	{
		indices[i] = i;
	}

	// Set up the description of the dynamic vertex buffer.
	vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
	vertexBufferDesc.ByteWidth = sizeof(FontVertexType) * mTextObj.Sentence->vertexCount;
	vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	vertexBufferDesc.MiscFlags = 0;
	vertexBufferDesc.StructureByteStride = 0;

	// Give the subresource structure a pointer to the vertex data.
	vertexData.pSysMem = vertices;
	vertexData.SysMemPitch = 0;
	vertexData.SysMemSlicePitch = 0;

	// Create the vertex buffer.
	result = device->GetDevice()->CreateBuffer(&vertexBufferDesc, &vertexData, &mTextObj.Sentence->vertexBuffer);
	CHECK(result);

	// Set up the description of the static index buffer.
	indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	indexBufferDesc.ByteWidth = sizeof(unsigned long) * mTextObj.Sentence->indexCount;
	indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	indexBufferDesc.CPUAccessFlags = 0;
	indexBufferDesc.MiscFlags = 0;
	indexBufferDesc.StructureByteStride = 0;

	// Give the subresource structure a pointer to the index data.
	indexData.pSysMem = indices;
	indexData.SysMemPitch = 0;
	indexData.SysMemSlicePitch = 0;

	// Create the index buffer.
	result = device->GetDevice()->CreateBuffer(&indexBufferDesc, &indexData, &mTextObj.Sentence->indexBuffer);
	CHECK(result);

	// Release the vertex array as it is no longer needed.
	delete [] vertices;
	vertices = 0;

	// Release the index array as it is no longer needed.
	delete [] indices;
	indices = 0;

	return true;
}

bool Text::UpdateSentenceType(const char* text, int positionX, int positionY, Color color)
{
	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	int numLetters = (int)strlen(text);

	// Check for possible buffer overflow.
	if(numLetters > mTextObj.Sentence->maxLength)
	{
		return false;
	}

	// Create the vertex array.
	FontVertexType* vertices = new FontVertexType[mTextObj.Sentence->vertexCount];

	// Initialize vertex array to zeros at first.
	memset(vertices, 0, (sizeof(FontVertexType) * mTextObj.Sentence->vertexCount));

	// Calculate the X and Y pixel position on the screen to start drawing to.
	float drawX = (float)(((device->GetResolutionInfo().ScreenWidth / 2) * -1) + positionX);
	float drawY = (float)((device->GetResolutionInfo().ScreenHeight / 2) - positionY);

	// Use the font class to build the vertex array from the sentence text and sentence draw location.
	mFont->BuildVertexArray((void*)vertices, text, drawX, drawY);

	D3D11_MAPPED_SUBRESOURCE mappedResource;
	// Lock the vertex buffer so it can be written to.
	HRESULT result = device->GetDeviceContext()->Map(mTextObj.Sentence->vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
	CHECK(result);

	// Get a pointer to the data in the vertex buffer.
	FontVertexType* verticesPtr = (FontVertexType*)mappedResource.pData;

	// Copy the data into the vertex buffer.
	memcpy(verticesPtr, (void*)vertices, (sizeof(FontVertexType) * mTextObj.Sentence->vertexCount));

	// Unlock the vertex buffer.
	device->GetDeviceContext()->Unmap(mTextObj.Sentence->vertexBuffer, 0);

	// Release the vertex array as it is no longer needed.
	delete [] vertices;
	vertices = 0;

	return true;
}

void Text::SetText(int x_pos, int y_pos, const char* format, ... )
{
	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	
	va_list argList;
	va_start(argList, format);

	const unsigned int MAX_CHARS = 1023;
	static char buffer[MAX_CHARS + 1];
	memset(buffer, 0, sizeof(buffer));
	vsnprintf_s(buffer, _countof(buffer), _TRUNCATE, format, argList);

	mTextObj.Text = buffer;
	mTextObj.XPosition = x_pos;
	mTextObj.YPosition = y_pos;
	mTextObj.TextColor = XColors::White;
}